#ifndef AER_VIEW_HPP_
#define AER_VIEW_HPP_

#include "aerView3D.hpp"
#include <glm/gtc/matrix_transform.hpp> // for lookAt


namespace aer {

/// + ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~
///
/// Describes a view point in the 3D space
///
/// + ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~
class View : public View3D
{
  private:
    Vector3 m_position;
    Vector3 m_target;
    Vector3 m_direction;
    Vector3 m_up;
    
    Matrix4x4 m_viewMatrix;
    bool m_bRebuild;
    
    //Position3D *m_pTarget;
    
    
  public:
    View()
    {
      set( Vector3(0.0f, 0.0f, 0.0f), 
           Vector3(0.0f, 0.0f, -1.0f), 
           Vector3(0.0f, 1.0f, 0.0f));
    }
    
    View(const Vector3& position, const Vector3& target, const Vector3& up) 
    {
      set( position, target, up);
    }
    
    /// ===========
    /// + Setters +
    /// ===========
    void set(const Vector3& position, const Vector3& target, const Vector3& up)
    {
      setPosition(position);
      setTarget(target);
      m_up = up;
      m_bRebuild = true;
    }
    
    //void set(const Vector3& position, const Vector3& target);
    
    void setPosition(const Vector3& position)
    {
      m_position = position;
      m_bRebuild = true;
    }
    
    void setTarget(const Vector3& target)
    {
      m_target    = target;
      m_direction = glm::normalize(m_target - m_position);
      m_bRebuild  = true;
    }
    
    void setDirection(const Vector3& direction)
    {
      m_direction = glm::normalize(direction);
      m_target    = m_position + m_direction;//
      m_bRebuild  = true;
    }
    
    /// ===========
    /// + Getters +
    /// ===========   
    const Vector3& getPosition()  const { return m_position; }
    const Vector3& getDirection() const { return m_direction; }
    const Vector3& getUp()        const { return m_up; }
    
    const Matrix4x4& getViewMatrix()
    { 
      if (isDirty()) {
        build();
      }
      return m_viewMatrix;
    }
    
    bool isDirty() const { return m_bRebuild; }
    
    
  private:
    void build()
    {
      // TODO [re]compute UP
      
      m_viewMatrix = glm::lookAt( m_position, m_target, m_up);
      m_bRebuild = false;
    }
};

} // aer

#endif // AER_VIEW_HPP_
